home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / pcl / src-16f.lha / compiler / rt / macros.lisp < prev    next >
Encoding:
Text File  |  1991-11-09  |  18.2 KB  |  541 lines

  1. ;;; -*- Package: RT; Log: c.log -*-
  2. ;;;
  3. ;;; **********************************************************************
  4. ;;; This code was written as part of the CMU Common Lisp project at
  5. ;;; Carnegie Mellon University, and has been placed in the public
  6. ;;; domain.  If you want to use this code or any part of CMU Common
  7. ;;; Lisp, please contact Scott Fahlman (Scott.Fahlman@CS.CMU.EDU)
  8. ;;; **********************************************************************
  9. ;;;
  10. ;;; This file contains various useful macros for generating code for the IBM
  11. ;;; RT.
  12. ;;;
  13. ;;; Written by William Lott, Christopher Hoover, and Rob Maclachlin, and Bill
  14. ;;; Chiles.
  15. ;;;
  16.  
  17. (in-package "RT")
  18.  
  19.  
  20.  
  21. ;;; Instruction-like macros.
  22.  
  23. ;;; MOVE -- Interface.
  24. ;;;
  25. (defmacro move (dst src)
  26.   "Move src into dst unless they are LOCATION=."
  27.   (once-only ((n-dst dst)
  28.           (n-src src))
  29.     `(unless (location= ,n-dst ,n-src)
  30.        (inst move ,n-dst ,n-src))))
  31.  
  32. ;;; LOADW and STOREW -- Interface.
  33. ;;;
  34. ;;; Load and store words.
  35. ;;;
  36. (macrolet ((def-mem-op (op inst)
  37.         `(defmacro ,op (object base &optional (offset 0) (lowtag 0))
  38.            `(inst ,',inst ,object ,base
  39.               (- (ash ,offset word-shift) ,lowtag)))))
  40.   (def-mem-op loadw l)
  41.   (def-mem-op storew st))
  42.  
  43. ;;; LOAD-SYMBOL -- Interface.
  44. ;;;
  45. (defmacro load-symbol (reg symbol)
  46.   "Load a pointer to the static symbol into reg."
  47.   `(inst a ,reg null-tn (static-symbol-offset ,symbol)))
  48.  
  49. ;;; LOAD-SYMBOL-FUNCTION, STORE-SYMBOL-FUNCTION,
  50. ;;; LOAD-SYMBOL-VALUE, STORE-SYMBOL-VALUE        -- interface.
  51. ;;;
  52. (macrolet
  53.     ((frob (slot)
  54.        (let ((loader (intern (concatenate 'simple-string
  55.                       "LOAD-SYMBOL-"
  56.                       (string slot))))
  57.          (storer (intern (concatenate 'simple-string
  58.                       "STORE-SYMBOL-"
  59.                       (string slot))))
  60.          (offset (intern (concatenate 'simple-string
  61.                       "SYMBOL-"
  62.                       (string slot)
  63.                       "-SLOT")
  64.                  (find-package "VM"))))
  65.      `(progn
  66.         (defmacro ,loader (reg symbol)
  67.           `(inst l ,reg null-tn
  68.              (+ (static-symbol-offset ',symbol)
  69.             (ash ,',offset word-shift)
  70.             (- other-pointer-type))))
  71.         (defmacro ,storer (reg symbol)
  72.           `(inst st ,reg null-tn
  73.              (+ (static-symbol-offset ',symbol)
  74.             (ash ,',offset word-shift)
  75.             (- other-pointer-type))))))))
  76.   (frob value)
  77.   (frob function))
  78.  
  79. ;;; LOAD-TYPE -- Interface.
  80. ;;;
  81. ;;; Subtract the low-tag from the pointer and add one less than the address
  82. ;;; units per word to get us to the low byte since the RT is big-endian.
  83. ;;;
  84. (defmacro load-type (target source &optional (low-tag 0))
  85.   "Loads the type bits from the source pointer into target, where pointer has
  86.    the specified low-tag."
  87.   `(inst lc ,target ,source (- word-bytes 1 ,low-tag)))
  88.  
  89.  
  90.  
  91. ;;;; Call and Return.
  92.  
  93. ;;; Macros to handle the fact that we cannot use the machine native call and
  94. ;;; return instructions due to low-tag bits on pointers.
  95. ;;;
  96.  
  97. ;;; LISP-JUMP -- Interface.
  98. ;;;
  99. (defmacro lisp-jump (function lip)
  100.   "Jump to the lisp function.  Lip is the lisp interior pointer register
  101.    used as a temporary."
  102.   `(progn
  103.      (inst cal ,lip ,function (- (ash function-header-code-offset
  104.                       word-shift)
  105.                  function-pointer-type))
  106.      (inst bx ,lip)
  107.      (move code-tn ,function)))
  108.  
  109. ;;; LISP-RETURN -- Interface.
  110. ;;;
  111. (defmacro lisp-return (return-pc lip &key (offset 0) (frob-code t))
  112.   "Return to return-pc, an LRA.  Lip is the lisp interior pointer register
  113.    used as a temporary.  Offset is the number of words to skip at the target,
  114.    and it has to be small enough for whatever instruction used in here."
  115.   `(progn
  116.      (inst cal ,lip ,return-pc
  117.        (- (ash (1+ ,offset) word-shift) other-pointer-type))
  118.      ,@(if frob-code
  119.       `((inst bx ,lip)
  120.         (move code-tn ,return-pc))
  121.       `((inst b ,lip)))))
  122.  
  123. ;;; EMIT-RETURN-PC -- Interface.
  124. ;;;
  125. (defmacro emit-return-pc (label)
  126.   "Emit a return-pc header word, making sure it is aligned properly for our
  127.    low-tag bits since there are other-pointers referring to the LRA's.  label
  128.    is the label to use for this return-pc."
  129.   `(progn
  130.      (align lowtag-bits)
  131.      (emit-label ,label)
  132.      (inst lra-header-word)))
  133.  
  134.  
  135. ;;;; Stack TN's.
  136.  
  137. ;;; LOAD-STACK-TN, STORE-STACK-TN  --  Interface.
  138. ;;;
  139. ;;; Move a stack TN to a register and vice-versa.  If the VOP is supplied,
  140. ;;; we can load from (or store onto) the number stack also.
  141. ;;;
  142. (defmacro load-stack-tn (reg stack &optional vop)
  143.   (once-only ((n-reg reg)
  144.           (n-stack stack))
  145.     `(sc-case ,n-stack
  146.        (control-stack
  147.     (loadw ,n-reg cfp-tn (tn-offset ,n-stack)))
  148.        ,@(when vop
  149.        `(((unsigned-stack signed-stack base-char-stack sap-stack)
  150.           (loadw ,n-reg (current-nfp-tn ,vop) (tn-offset ,n-stack))))))))
  151. ;;;
  152. (defmacro store-stack-tn (reg stack &optional vop)
  153.   (once-only ((n-reg reg)
  154.           (n-stack stack))
  155.     `(sc-case ,n-stack
  156.        (control-stack
  157.     (storew ,n-reg cfp-tn (tn-offset ,n-stack)))
  158.        ,@(when vop
  159.        `(((unsigned-stack signed-stack base-char-stack sap-stack)
  160.           (storew ,n-reg (current-nfp-tn ,vop) (tn-offset ,n-stack))))))))
  161.  
  162.  
  163. ;;; MAYBE-LOAD-STACK-TN  --  Interface.
  164. ;;;
  165. (defmacro maybe-load-stack-tn (reg reg-or-stack)
  166.   "Move the TN Reg-Or-Stack into Reg if it isn't already there."
  167.   (once-only ((n-reg reg)
  168.           (n-stack reg-or-stack))
  169.     `(sc-case ,n-reg
  170.        ((any-reg descriptor-reg non-descriptor-reg word-pointer-reg)
  171.     (sc-case ,n-stack
  172.       ((any-reg descriptor-reg non-descriptor-reg word-pointer-reg)
  173.        (move ,n-reg ,n-stack))
  174.       ((control-stack)
  175.        (loadw ,n-reg cfp-tn (tn-offset ,n-stack))))))))
  176.  
  177.  
  178.  
  179. ;;;; Storage allocation:
  180.  
  181. ;;; WITH-FIXED-ALLOCATION -- Internal Interface.
  182. ;;;
  183. (defmacro with-fixed-allocation ((result-tn header-tn alloc-tn type-code size)
  184.                  &body body)
  185.   "Do stuff to allocate an other-pointer object of fixed Size with a single
  186.    word header having the specified Type-Code.  The result is placed in
  187.    Result-TN.  Header-Tn is any register temp which this uses to hold the
  188.    header of the allocated object before storing it into the header slot.
  189.    Alloc-tn is a register to hold the heap pointer, and it can be any register
  190.    since raw heap pointers look like fixnums (dual-word aligned objects).  The
  191.    body is placed inside PSEUDO-ATOMIC, and presumably initializes the object."
  192.   `(progn
  193.      (pseudo-atomic (,header-tn)
  194.        (load-symbol-value ,alloc-tn *allocation-pointer*)
  195.        ;; Take free pointer and make descriptor pointer.
  196.        ;; Just add in three low-tag bits since alloc ptr is dual-word aligned.
  197.        (inst cal ,result-tn ,alloc-tn other-pointer-type)
  198.        (inst cal ,alloc-tn ,alloc-tn (pad-data-block ,size))
  199.        (store-symbol-value ,alloc-tn *allocation-pointer*)
  200.        (inst li ,header-tn (logior (ash (1- ,size) type-bits) ,type-code))
  201.        (storew ,header-tn ,result-tn 0 other-pointer-type)
  202.        ,@body)
  203.      (load-symbol-value ,header-tn *internal-gc-trigger*)
  204.      (inst tlt ,header-tn ,alloc-tn)))
  205.  
  206.  
  207.  
  208. ;;;; Type testing noise.
  209.  
  210. ;;; GEN-RANGE-TEST -- Internal.
  211. ;;;
  212. ;;; Generate code that branches to TARGET iff REG contains one of VALUES.  If
  213. ;;; NOT-P is true, invert the test.  Jumping to DROP-THROUGH is the same as
  214. ;;; falling out the bottom.
  215. ;;;
  216. (defun gen-range-test (reg target drop-through not-p values
  217.                &optional (separation 1) (min 0))
  218.   (let ((tests nil)
  219.     (start nil)
  220.     (end nil)
  221.     (insts nil))
  222.     ;; Block off list of values into ranges, so we can test these as intervals
  223.     ;; instead of individually.
  224.     (flet ((emit-test ()
  225.               (if (= start end)
  226.               (push start tests)
  227.               (push (cons start end) tests))))
  228.       (dolist (value values)
  229.     (cond ((< value min)
  230.            (error "~S is less than the specified minimum of ~S"
  231.               value min))
  232.           ((null start)
  233.            (setf start value))
  234.           ((> value (+ end separation))
  235.            (emit-test)
  236.            (setf start value)))
  237.     (setf end value))
  238.       (emit-test))
  239.     ;; Output tests, dealing with not-p.
  240.     (macrolet ((inst (name &rest args)
  241.              `(push (list 'inst ',name ,@args) insts)))
  242.       (do ((remaining (nreverse tests) (cdr remaining)))
  243.       ((null remaining))
  244.     (let ((test (car remaining))
  245.           (last (null (cdr remaining))))
  246.       (cond
  247.        ((atom test)
  248.         (inst c reg test)
  249.         (if last
  250.         (if not-p
  251.             ;; We compared for the thing.  If not-p, since it is the
  252.             ;; last thing to look for, and we don't want it, then goto
  253.             ;; target.
  254.             (inst bnc :eq target)
  255.             ;; If we do want the thing, and we got it, then goto
  256.             ;; target.
  257.             (inst bc :eq target))
  258.         ;; If it is not the last thing, and it is not not-p, and we
  259.         ;; have it, then go to target.  If not-p, and we have the thing
  260.         ;; we don't want, then drop through.
  261.         (inst bc :eq (if not-p drop-through target))))
  262.        (t
  263.         (let ((start (car test))
  264.           (end (cdr test)))
  265.           ;; We don't need this code if start is the smallest value we
  266.           ;; could possibly have.  We know reg can't be less than start.
  267.           (unless (= start min)
  268.         (inst c reg start)
  269.         ;; If I want the range, and I'm less than the start, then
  270.         ;; drop through.  If I don't want the range, goto target
  271.         ;; because I'm not in the range.
  272.         (inst bc :lt (if not-p target drop-through)))
  273.           ;; We know reg is greater than or equal to start, so see if it
  274.           ;; is less than or equal to end.
  275.           (inst c reg end)
  276.           (if last
  277.           (if not-p
  278.               ;; If this range is the last test, and I don't
  279.               ;; want it, and I'm not in it, then goto target.
  280.               (inst bc :gt target)
  281.               ;; If this range is the last test, and I want the
  282.               ;; range, and I'm in it, then goto target.
  283.               (inst bnc :gt target))
  284.           (inst bnc :gt (if not-p drop-through target)))))))))
  285.     (nreverse insts)))
  286.  
  287. (defconstant type-separation 4)
  288. (defconstant min-type (+ other-immediate-0-type lowtag-limit))
  289.  
  290. ;;; TEST-TYPE-AUX -- Internal.
  291. ;;;
  292. (defun test-type-aux (reg temp target drop-through not-p lowtags immed hdrs
  293.               function-p)
  294.   (let* ((fixnump (and (member even-fixnum-type lowtags :test #'eql)
  295.                (member odd-fixnum-type lowtags :test #'eql)))
  296.      (lowtags (sort (if fixnump
  297.                 (delete even-fixnum-type
  298.                     (remove odd-fixnum-type lowtags
  299.                         :test #'eql)
  300.                     :test #'eql)
  301.                 (copy-list lowtags))
  302.             #'<))
  303.      (lowtag (if function-p
  304.              vm:function-pointer-type
  305.              vm:other-pointer-type))
  306.      (hdrs (sort (copy-list hdrs) #'<))
  307.      (immed (sort (copy-list immed) #'<)))
  308.     (append
  309.      (when immed
  310.        `((inst nilz ,temp ,reg type-mask)
  311.      ,@(if (or fixnump lowtags hdrs)
  312.            ;; If we're going to output any tests below, fall through to
  313.            ;; those tests from these.
  314.            (let ((fall-through (gensym)))
  315.          `((let (,fall-through (gen-label))
  316.              ,@(gen-range-test
  317.             temp (if not-p drop-through target)
  318.             fall-through nil immed type-separation)
  319.              (emit-label ,fall-through))))
  320.            ;; If there are no other tests, drop through to the end of all
  321.            ;; these tests.
  322.            (gen-range-test temp target drop-through not-p
  323.                    immed type-separation))))
  324.      (when fixnump
  325.        `((inst nilz ,temp ,reg 3)
  326.      ,(if (or lowtags hdrs)
  327.           ;; If more tests follow this one, fall through to them.
  328.           `(inst bc :eq ,(if not-p drop-through target))
  329.           ;; If no more tests follow, drop totally out of here.
  330.           `(inst ,(if not-p 'bnc 'bc) :eq ,target))))
  331.      (when (or lowtags hdrs)
  332.        `((inst nilz ,temp ,reg lowtag-mask)))
  333.      (when lowtags
  334.        (if hdrs
  335.        ;; If we're going to output any tests below, fall through to
  336.        ;; those tests from these.
  337.        (let ((fall-through (gensym)))
  338.          `((let ((,fall-through (gen-label)))
  339.          ,@(gen-range-test temp (if not-p drop-through target)
  340.                    fall-through nil lowtags)
  341.          (emit-label ,fall-through))))
  342.        (gen-range-test temp target drop-through not-p lowtags)))
  343.      (when hdrs
  344.        `((inst c ,temp ,lowtag)
  345.      (inst bnc :eq ,(if not-p target drop-through))
  346.      (load-type ,temp ,reg ,lowtag)
  347.      ,@(gen-range-test temp target drop-through not-p
  348.                hdrs type-separation min-type))))))
  349.  
  350. (defconstant immediate-types
  351.   (list base-char-type unbound-marker-type))
  352.  
  353. (defconstant function-subtypes
  354.   (list funcallable-instance-header-type closure-header-type
  355.     function-header-type closure-function-header-type))
  356.  
  357. ;;; TEST-TYPE -- Interface.
  358. ;;;
  359. ;;; This is used in type-vops.lisp.
  360. ;;;
  361. (defmacro test-type (register temp target not-p &rest type-codes)
  362.   "Register holds a descriptor object that might have one of the types in
  363.    type-codes.  If it does, goto target; otherwise, fall through.  If not-p is
  364.    set, goto target when register does not hold one of the types.  Type-codes
  365.    must be compile-time constants that this evaluates to get numeric codes.
  366.    Temp is a non-descriptor temporary needed by the code returned by the
  367.    macro."
  368.   (let* ((type-codes (mapcar #'eval type-codes))
  369.      (lowtags (remove lowtag-limit type-codes :test #'<))
  370.      (extended (remove lowtag-limit type-codes :test #'>))
  371.      (immediates (intersection extended immediate-types :test #'eql))
  372.      (headers (set-difference extended immediate-types :test #'eql))
  373.      (function-p nil))
  374.     (unless type-codes
  375.       (error "Must supply at least on type for test-type."))
  376.     (when (and headers (member other-pointer-type lowtags))
  377.       (warn "OTHER-POINTER-TYPE supersedes the use of ~S" headers)
  378.       (setf headers nil))
  379.     (when (and immediates
  380.            (or (member other-immediate-0-type lowtags)
  381.            (member other-immediate-1-type lowtags)))
  382.       (warn "OTHER-IMMEDIATE-n-TYPE supersedes the use of ~S" immediates)
  383.       (setf immediates nil))
  384.     (when (intersection headers function-subtypes)
  385.       (unless (subsetp headers function-subtypes)
  386.     (error "Can't test for mix of function subtypes and normal ~
  387.         header types."))
  388.       (setq function-p t))
  389.     (let ((n-reg (gensym))
  390.       (n-temp (gensym))
  391.       (n-target (gensym))
  392.       (drop-through (gensym)))
  393.       `(let ((,n-reg ,register)
  394.          (,n-temp ,temp)
  395.          (,n-target ,target)
  396.          (,drop-through (gen-label)))
  397.      (declare (ignorable ,n-temp))
  398.      ,@(if (constantp not-p)
  399.            (test-type-aux n-reg n-temp n-target drop-through
  400.                   (eval not-p) lowtags immediates headers
  401.                   function-p)
  402.            `((cond (,not-p
  403.             ,@(test-type-aux n-reg n-temp n-target drop-through t
  404.                      lowtags immediates headers
  405.                      function-p))
  406.                (t
  407.             ,@(test-type-aux n-reg n-temp n-target drop-through nil
  408.                      lowtags immediates headers
  409.                      function-p)))))
  410.      (emit-label ,drop-through)))))
  411.  
  412.  
  413.  
  414. ;;;; Error Code
  415.  
  416. (defvar *adjustable-vectors* nil)
  417.  
  418. (defmacro with-adjustable-vector ((var) &rest body)
  419.   `(let ((,var (or (pop *adjustable-vectors*)
  420.            (make-array 16
  421.                    :element-type '(unsigned-byte 8)
  422.                    :fill-pointer 0
  423.                    :adjustable t))))
  424.      (setf (fill-pointer ,var) 0)
  425.      (unwind-protect
  426.      (progn
  427.        ,@body)
  428.        (push ,var *adjustable-vectors*))))
  429.  
  430. (eval-when (compile load eval)
  431.   (defun emit-error-break (vop kind code values)
  432.     (let ((vector (gensym)))
  433.       `((let ((vop ,vop))
  434.       (when vop
  435.         (note-this-location vop :internal-error)))
  436.     (inst break ,kind)
  437.     (with-adjustable-vector (,vector)
  438.       (write-var-integer (error-number-or-lose ',code) ,vector)
  439.       ,@(mapcar #'(lambda (tn)
  440.             `(let ((tn ,tn))
  441.                (write-var-integer (make-sc-offset (sc-number
  442.                                    (tn-sc tn))
  443.                                   (tn-offset tn))
  444.                           ,vector)))
  445.             values)
  446.       (inst byte (length ,vector))
  447.       (dotimes (i (length ,vector))
  448.         (inst byte (aref ,vector i))))
  449.     (align word-shift)))))
  450.  
  451. (defmacro error-call (vop error-code &rest values)
  452.   "Cause an error.  ERROR-CODE is the error to cause."
  453.   (cons 'progn
  454.     (emit-error-break vop error-trap error-code values)))
  455.  
  456.  
  457. ;;; CERROR-CALL -- Internal Interface.
  458. ;;;
  459. ;;; Output the error break stuff followed by a branch to the continuable code.
  460. ;;; The break handler can skip the error break data, setting the pc in the
  461. ;;; signal context to our following branch, and if we continue, we're setup.
  462. ;;;
  463. (defmacro cerror-call (vop label error-code &rest values)
  464.   "Cause a continuable error.  If the error is continued, execution resumes at
  465.   LABEL."
  466.   `(progn
  467.      ,@(emit-error-break vop cerror-trap error-code values)
  468.      (inst b ,label)))
  469.  
  470. (defmacro generate-error-code (vop error-code &rest values)
  471.   "Generate-Error-Code Error-code Value*
  472.   Emit code for an error with the specified Error-Code and context Values."
  473.   `(assemble (*elsewhere*)
  474.      (let ((start-lab (gen-label)))
  475.        (emit-label start-lab)
  476.        (error-call ,vop ,error-code ,@values)
  477.        start-lab)))
  478.  
  479. (defmacro generate-cerror-code (vop error-code &rest values)
  480.   "Generate-CError-Code Error-code Value*
  481.   Emit code for a continuable error with the specified Error-Code and
  482.   context Values.  If the error is continued, execution resumes after
  483.   the GENERATE-CERROR-CODE form."
  484.   (let ((continue (gensym "CONTINUE-LABEL-"))
  485.     (error (gensym "ERROR-LABEL-")))
  486.     `(let ((,continue (gen-label)))
  487.        (emit-label ,continue)
  488.        (assemble (*elsewhere*)
  489.      (let ((,error (gen-label)))
  490.        (emit-label ,error)
  491.        (cerror-call ,vop ,continue ,error-code ,@values)
  492.        ,error)))))
  493.  
  494.  
  495.  
  496. ;;;; PSEUDO-ATOMIC.
  497.  
  498. ;;; PSEUDO-ATOMIC -- Internal Interface.
  499. ;;;
  500. (defmacro pseudo-atomic ((ndescr-temp) &rest forms)
  501.   (let ((label (gensym "LABEL-")))
  502.     `(let ((,label (gen-label)))
  503.        (inst li ,ndescr-temp 0)
  504.        (store-symbol-value ,ndescr-temp lisp::*pseudo-atomic-interrupted*)
  505.        ;; Note: we just use cfp as some not-zero value.
  506.        (store-symbol-value cfp-tn lisp::*pseudo-atomic-atomic*)
  507.        ,@forms
  508.        (inst li ,ndescr-temp 0)
  509.        (store-symbol-value ,ndescr-temp lisp::*pseudo-atomic-atomic*)
  510.        (load-symbol-value ,ndescr-temp lisp::*pseudo-atomic-interrupted*)
  511.        (inst c ,ndescr-temp 0)
  512.        (inst bc :eq ,label)
  513.        (inst break pending-interrupt-trap)
  514.        (emit-label ,label))))
  515.  
  516. ;;;; Float stuff:
  517. ;;;
  518. ;;;    Since moving between memory and a FP register reqires *two* temporaries,
  519. ;;; we need a special temporary to form the magic address we store to do a
  520. ;;; floating point operation.  We get this temp by always spilling NL0 on the
  521. ;;; number stack.  This appears rather grody, but actually the 68881 is so slow
  522. ;;; compared to the ROMP that this overhead is not very great.
  523. ;;;
  524. ;;; Note: The RT interrupt handler preserves 64 bytes beyond the current stack
  525. ;;; pointer, so we don't need to dink the stack pointer.  We can just use the
  526. ;;; space beyond it.
  527. ;;;
  528. ;;; We also use LIP to form the address of the data location that we are
  529. ;;; reading or writing.
  530.  
  531. (defvar *in-with-fp-temp* nil)
  532.  
  533. (defmacro with-fp-temp ((var) &body body)
  534.   `(if *in-with-fp-temp*
  535.        (error "Can only have one FP temp.")
  536.        (let ((,var nl0-tn)
  537.          (*in-with-fp-temp* t))
  538.      (storew ,var nsp-tn -1)
  539.      (multiple-value-prog1 (progn ,@body)
  540.        (loadw ,var nsp-tn -1)))))
  541.